package com.seleniti.enpareja.web.desktop.server.model.integration.service.impl.appengine;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import org.ioc.commons.utils.Format;
import org.ioc.commons.utils.jdo.JdoQueryHelper;
import org.ioc.commons.utils.logs.Log4JFormatterLogger;

import com.seleniti.enpareja.controller.common.domain.LoggedUserInfo;
import com.seleniti.enpareja.controller.common.domain.Page;
import com.seleniti.enpareja.controller.common.exception.EnParejaException;
import com.seleniti.enpareja.controller.common.exception.EnParejaException.ErrorCode;
import com.seleniti.enpareja.controller.model.integration.database.domain.Couple;
import com.seleniti.enpareja.controller.model.integration.database.domain.CoupleRequest;
import com.seleniti.enpareja.controller.model.integration.database.domain.NotificationType;
import com.seleniti.enpareja.controller.model.integration.database.domain.CoupleRequest.Status;
import com.seleniti.enpareja.controller.model.integration.database.domain.CoupleType;
import com.seleniti.enpareja.controller.model.integration.database.domain.User;

public class GAECoupleService extends GAEService {
	/**
	 * Logger for this class
	 */
	private static final Log4JFormatterLogger logger = Log4JFormatterLogger.getLogger(GAECoupleService.class);

	public GAECoupleService() {
		logger.info("GAE Couple service instance");
	}

	public Couple find(String id) throws EnParejaException {
		assertLoggedIn("Find couple id");

		try {
			return pm.getObjectById(Couple.class, id);
		} catch (Exception e) {
			return null;
		}
	}

	public String save(Couple couple) throws EnParejaException {
		assertThisLoggedCouple(couple, "Save couple");

		logger.info("Saving {0}...", couple);
		pm.makePersistent(couple);
		String coupleId = couple.getCoupleId();
		logger.info("... saved OK {0}", couple);
		return coupleId;
	}

	public void delete(Couple couple) throws EnParejaException {
		assertLoggedIn("Delete couple");

		pm.deletePersistent(couple);

	}

	public CoupleRequest makeCoupleRequest(String requestToUserId, String requestFromUserId) {
		CoupleRequest cr = new CoupleRequest();

		User from = pm.getObjectById(User.class, requestFromUserId);
		User to = pm.getObjectById(User.class, requestToUserId);

		cr.setFrom(from);
		cr.setTo(to);
		cr.setStatus(Status.PENDING);

		pm.makePersistent(cr);

		return cr;
	}

	public Couple makeCouple(CoupleRequest coupleRequest) throws EnParejaException {

		String userEmail1 = coupleRequest.getTo().getUserId();
		String userEmail2 = coupleRequest.getFrom().getUserId();

		logger.info("Making couple {0} with {1}", userEmail1, userEmail2);

		Couple couple = new Couple();

		User member1 = getUserWithoutCouple(userEmail1/*, false*/);
		User member2 = getUserWithoutCouple(userEmail2/*, false*/);

		couple.setCoupleType(CoupleType.USERS_COUPLE);
		couple.setMember1(member1);
		couple.setMember2(member2);

		member1.setCouple(member2);
		member1.setCoupleType(couple.getCoupleType());

		member2.setCouple(member1);
		member2.setCoupleType(couple.getCoupleType());

		pm.makePersistent(couple);

		/*
		 * We delete all requests
		 */
		member1.getRequestsToYou().clear();
		member2.getRequestsToYou().clear();
		
		new GAENotificationService(this).notifyUser(NotificationType.GOT_COUPLED, member2, member1);

		// logger.info("\nAfter making couple: {0}\nGetting logged user info: {1}",
		// couple, getLoggedUserInfo());

		return couple;
	}

	protected User getUserWithoutCouple(String userId/*, boolean forceBreakUp*/) throws EnParejaException {
		User user = pm.getObjectById(User.class, userId);

		if (user == null) {
			throw new EnParejaException(ErrorCode.USER_NOT_FOUND, Format.substitute(
					"No existe ningún usuario con email {0}", userId));
		}

		if (user.getCouple() != null) {
			if (user.getCoupleType()==CoupleType.SINGLE_USER || user.getCoupleType()==CoupleType.TESTING_MODE_COUPLE /*|| forceBreakUp*/) {
				breakUp(user);
			} else {
				throw new EnParejaException(ErrorCode.USER_ALREADY_HAS_A_COUPLE, Format.substitute(
						"El usuario {0} ya tiene pareja. No se puede emparejar de nuevo.", user.getNickName()));
			}
		}
		return user;
	}

	public Couple makeSingle(String userId) throws EnParejaException {
		User user = getUserWithoutCouple(userId/*, true*/);

		String userEmail = user.getUserEmail();
		logger.info("Making single {0}", userEmail);

		Couple couple = new Couple();

		User fakeuser = generateFakeUser();

		couple.setCoupleType(CoupleType.SINGLE_USER);
		couple.setMember1(user);
		couple.setMember2(fakeuser);

		user.setCouple(fakeuser);
		user.setCoupleType(couple.getCoupleType());

		fakeuser.setCouple(user);
		fakeuser.setCoupleType(couple.getCoupleType());

		pm.makePersistent(couple);

		/*
		 * We delete all requests
		 */
		user.getRequestsToYou().clear();
		fakeuser.getRequestsToYou().clear();

		return couple;
	}

	public Couple makeTestCouple(String userId) throws EnParejaException {
		User user = getUserWithoutCouple(userId/*, true*/);

		String userEmail = user.getUserEmail();
		logger.info("Making test couple {0}", userEmail);

		Couple couple = new Couple();

		User fakeuser = generateFakeUser();

		couple.setCoupleType(CoupleType.TESTING_MODE_COUPLE);
		couple.setMember1(user);
		couple.setMember2(fakeuser);

		user.setCouple(fakeuser);
		user.setCoupleType(couple.getCoupleType());
		fakeuser.setCouple(user);
		fakeuser.setCoupleType(couple.getCoupleType());

		pm.makePersistent(couple);

		/*
		 * We delete all requests
		 */
		user.getRequestsToYou().clear();
		fakeuser.getRequestsToYou().clear();

		return couple;
	}

	public Page<CoupleRequest> getAllCoupleRequestsPage(long start, long length) {

		Page<CoupleRequest> page = new Page<>();
		JdoQueryHelper qh = JdoQueryHelper.newQuery(pm, CoupleRequest.class);

		qh.setResult("count(this)");
		Long total = (Long) qh.execute();

		qh.setRange(start, start + length);
		qh.setResult("this");

		@SuppressWarnings("unchecked")
		List<CoupleRequest> elms = (List<CoupleRequest>) qh.execute();

		page.setTotal(total != null ? total.longValue() : 0l);
		page.setElements(elms);

		return page;
	}

	public Page<CoupleRequest> getCoupleRequestsToOrFromUserPage(String userId, long start, long length) {

		Page<CoupleRequest> page = new Page<>();
		JdoQueryHelper qh1 = JdoQueryHelper.newQuery(pm, CoupleRequest.class);
		JdoQueryHelper qh2 = JdoQueryHelper.newQuery(pm, CoupleRequest.class);

		qh1.orParams("to==%s", userId);
		qh2.orParams("from==%s", userId);

		qh1.setResult("count(this)");
		qh2.setResult("count(this)");

		Long total1 = (Long) qh1.execute();
		Long total2 = (Long) qh2.execute();

		qh1.setResult("this");
		qh2.setResult("this");

		@SuppressWarnings("unchecked")
		List<CoupleRequest> elms1 = (List<CoupleRequest>) qh1.execute();
		@SuppressWarnings("unchecked")
		List<CoupleRequest> elms2 = (List<CoupleRequest>) qh2.execute();

		page.setTotal((total1 != null ? total1.longValue() : 0l) + (total2 != null ? total2.longValue() : 0l));

		List<CoupleRequest> elms = new ArrayList<>();
		elms.addAll(elms1);
		elms.addAll(elms2);

		if (elms.size() > 0) {
			page.setElements(elms.subList((int) Math.min(start, elms.size() - 1),
					(int) Math.min(start + length, elms.size())));
		} else {
			page.setElements(elms);
		}

		return page;
	}

	public void dismissCoupleRequest(String userIdFrom, String userIdTo) throws EnParejaException {
		LoggedUserInfo lui = assertLoggedIn("Dismiss couple request");

		JdoQueryHelper qh = JdoQueryHelper.newQuery(pm, CoupleRequest.class);

		qh.andParams("from==%s", userIdFrom);
		qh.andParams("to==%s", userIdTo);

		@SuppressWarnings("unchecked")
		List<CoupleRequest> elms = (List<CoupleRequest>) qh.execute();

		if (!lui.getUserId().equals(userIdFrom)) {
			throw new EnParejaException(ErrorCode.PERMISSION_DENIED);
		}

		if (elms != null && !elms.isEmpty()) {
			pm.deletePersistentAll(elms);
		}
	}

	public void dismissCoupleRequest(CoupleRequest request) throws EnParejaException {
		LoggedUserInfo lui = assertLoggedIn("Dismiss couple request");

		if (!lui.getUserId().equals(request.getFrom().getUserId())) {
			throw new EnParejaException(ErrorCode.PERMISSION_DENIED);
		}

		pm.deletePersistent(request);
	}

	public void rejectCoupleRequest(CoupleRequest request) throws EnParejaException {
		LoggedUserInfo lui = assertLoggedIn("Reject couple request");

		if (!lui.getUserId().equals(request.getTo().getUserId())) {
			throw new EnParejaException(ErrorCode.PERMISSION_DENIED);
		}

		request.setStatus(Status.REJECTED);
	}

	public void breakUp(User user) {
		Couple couple = lookForCouple(user);
		if (couple != null) {
			User member1 = couple.getMember1();
			User member2 = couple.getMember2();
			if (!member1.isFakeUser()) {
				member1.setCouple(null);
				member1.setCoupleType(null);
				
				if(!member1.getUserId().equals(user.getUserId())) {
					new GAENotificationService(this).notifyUser(NotificationType.BROKE_UP, member1, user);
				}
			} else {
				pm.deletePersistent(member1);
			}
			if (!member2.isFakeUser()) {
				member2.setCouple(null);
				member2.setCoupleType(null);
				if(!member2.getUserId().equals(user.getUserId())) {
					new GAENotificationService(this).notifyUser(NotificationType.BROKE_UP, member2, user);
				}
			} else {
				pm.deletePersistent(member2);
			}
			pm.deletePersistent(couple);
		}
	}

	private User generateFakeUser() {
		User fake = new User();

		String userEmail = Format.substitute("{0}@fakeuser.enpareja.net", UUID.randomUUID());

		fake.setUserEmail(userEmail);
		fake.setNickName(userEmail);
		fake.setFakeUser(true);

		return fake;
	}

}
